package com.whately.core.redis.utils;

import com.whately.core.base.model.RedisKeyBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisCacheZSetUtils {

    private final Logger logger = LoggerFactory.getLogger(RedisCacheZSetUtils.class);

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedisTemplateUtils redisTemplateUtils;

    /**************************************************************************
     *     z-set 模拟pop
     *************************************************************************/
    private static final String ZSET_LPOP_SCRIPT = "local result = redis.call('ZRANGE', KEYS[1], 0, 0) " +
            "local element = result[1] " +
            "if element then " +
            "    redis.call('ZREM', KEYS[1], element) " +
            "    return element " +
            "else " +
            "    return nil " +
            "end ";

    private static final String ZSET_LPOP_SCRIPT_WITH_SCORE = "local result = redis.call('ZRANGE', KEYS[1], 0, 0,'WITHSCORES') " +
            "local element = result[1] " +
            "local element2 = result[2] " +
            "if element then " +
            "    redis.call('ZREM', KEYS[1], element) " +
            "    return {element,element2} " +
            "else " +
            "    return nil " +
            "end ";


    public String zsetLPop(RedisKeyBuilder key){
        return redisTemplate.execute(new DefaultRedisScript<>(ZSET_LPOP_SCRIPT, String.class), Collections.singletonList(key.getKey()));
    }

    public RedisZsetTypedBean zsetLPopWithScore(RedisKeyBuilder key){
        List<String> ls= redisTemplate.execute(new DefaultRedisScript<>(ZSET_LPOP_SCRIPT_WITH_SCORE, List.class), Collections.singletonList(key.getKey()));
        if(ls!=null&&ls.size()==2){
            RedisZsetTypedBean bean = new RedisZsetTypedBean();
            bean.setHashKey(ls.get(0));
            try{
                bean.setScoreDouble(Double.parseDouble(ls.get(1)));
                bean.setScore(bean.getScoreDouble().longValue());
            }catch (Exception e){
                logger.error("zsetLPopWithScore error",e);
            }
            return bean;
        }
        return null;
    }


    /**************************************************************************
     *     z-set
     *************************************************************************/
    public Boolean addAndExpire(RedisKeyBuilder key, String value, double score, long timeout, TimeUnit timeUnit) {
        Boolean b = redisTemplate.opsForZSet().add(key.getKey(),value,score);
        if(-1==redisTemplateUtils.ttl(key)){
            redisTemplateUtils.expire(key,timeout,timeUnit);
        }
        return b;
    }

    public Long addAndExpire(RedisKeyBuilder key, Set<ZSetOperations.TypedTuple<String>> typedTuples, long timeout, TimeUnit timeUnit) {
        if(typedTuples == null || typedTuples.size() == 0){
            return 0l;
        }
        Long l= redisTemplate.opsForZSet().add(key.getKey(),typedTuples);
        if(-1==redisTemplateUtils.ttl(key)){
            redisTemplateUtils.expire(key,timeout,timeUnit);
        }
        return l;
    }

    public Long remove(RedisKeyBuilder key, Object... values) {
        return redisTemplate.opsForZSet().remove(key.getKey(),values);
    }

    public Long removeRange(RedisKeyBuilder key, long start, long end) {
        Long result = redisTemplate.opsForZSet().removeRange(key.getKey(), start, end);
        return result;
    }

    public Long removeRangeByScore(RedisKeyBuilder key, double min, double max) {
        Long result = redisTemplate.opsForZSet().removeRangeByScore(key.getKey(), min, max);
        return result;
    }

    public Double score(RedisKeyBuilder key, Object o){
        return redisTemplate.opsForZSet().score(key.getKey(),o);
    }

    public Long size(RedisKeyBuilder key) {
        return redisTemplate.opsForZSet().size(key.getKey());
    }

    public Set<String> range(RedisKeyBuilder key, long start, long end) {
        return redisTemplate.opsForZSet().range(key.getKey(), start, end);
    }

    public Set<String> reverseRange(RedisKeyBuilder key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key.getKey(), start, end);
    }

    public Set<String> rangeByScore(RedisKeyBuilder key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(key.getKey(), min, max);
    }

    public Double incrementScore(RedisKeyBuilder key, String o, double delta) {
        return redisTemplate.opsForZSet().incrementScore(key.getKey(), o, delta);
    }

}
